



/**
# Clocker
计时器，可用于正计时、倒计时

@param target : Date     目标时间，在正计时中，目标时间是计时的起始时间；在倒计时中，目标时间为结束时间；默认为当前时间
@param countDown : boolean      是否是倒计时，默认为 false


# 使用示例
```
let startDate = new Date();
let clocker = new Clocker(startDate);

// 获得计时器的 时、分、秒
let hours = clocker.hours;
let minutes = clocker.minutes;
let seconds = clocker.seconds;
```
 */
class Clocker {


    /**
     * 目标时间，在正计时中，目标时间是计时的起始时间；在倒计时中，目标时间为结束时间；默认为当前时间
     */
    protected _target!:Date;

    set target(newVal){
        if (newVal !== this._target){
            this._target = newVal;
            this.launchListenReachTarget();
        }
    }

    get target():Date{
        return this._target || (this.target = new Date())
    }
    
    /**
     * 是否是倒计时，默认为 false
     */
    protected _countDown:boolean = false;

    set countDown(newVal){
        const newValB = Boolean(newVal)
        if (newValB != this._countDown){
            this._countDown = newValB;
            this.launchListenReachTarget();
        }
    }

    get countDown() {
        return this._countDown
    }


    /**
     * 构造器
     * @param target : Date     目标时间，在正计时中，目标时间是计时的起始时间；在倒计时中，目标时间为结束时间；默认为当前时间
     * @param countDown : boolean      是否是倒计时，默认为 false
     * @memberof Clocker
     */
    constructor(target?:Date, countDown?:boolean) {
        target && (this.target = target);
        this.countDown = countDown as boolean;
    }



    /**
     * 计时的时间
     * 
     * @readonly : number
     * @memberof Clocker
     */
    get timingTime() {
        const factor = this.countDown ? -1 : 1;
        const interval = this._intervalFromTarget * factor;
        return interval;
    }



    /**
     * 从目标时间 到现在 的时间间隔
     * @private
     */
    protected get _intervalFromTarget(){
        return  Date.now() - this.target.getTime();
    }

    /**
     * 是否已经到达目标时间
     */
    get isReachedTarget(){
        return this._intervalFromTarget >= 0;
    }

    /**
     * 计时是否正在正常地进行中
     * @readonly : boolean
     * @memberof Clocker
     * 
     * 注意：
     * 在正计时中，返回 `false` 表示：还未到达开始计时时间；
     * 在倒计时中，返回 `false` 表示：已经到达结束时间；
     */
    get isCounting() {
        return this.timingTime >= 0;
    }


    /**
     * 计时的因数
     * 
     * @readonly : number
     * @memberof Clocker
     */
    protected get _countFactor() {
        return this.isCounting ? 1 : -1;
    }


    //计时信息：开始

    /**
     * 获得已计时的 Date 对象 (以世界标准时间(UTC)计时)
     * @readonly  : Date
     */
    get timedDate() {
        const interval = Math.abs(this.timingTime);
        return new Date(interval);
    }




    /**
     * 获得计时的年数
     * @readonly : number
     */
    get year() {
        return (this.timedDate.getUTCFullYear() - 1970) * this._countFactor;
    }

    /**
     * 获得计时的月数
     * @readonly : number
     */
    get month() {
        return this.timedDate.getUTCMonth() * this._countFactor;
    }


    /**
     * 获得计时的天数
     * @readonly : number
     */
    get date() {
        return (this.timedDate.getUTCDate() - 1) * this._countFactor;
    }



    /**
     * 获得计时的小时数
     * @readonly : number
     */
    get hours() {
        return this.timedDate.getUTCHours() * this._countFactor;
    }

    /**
     * 获得计时的分钟数
     * @readonly : number
     */
    get minutes() {
        return this.timedDate.getUTCMinutes() * this._countFactor;
    }

    /**
     * 获得计时的秒钟数
     * @readonly : number
     */
    get seconds() {
        return this.timedDate.getUTCSeconds() * this._countFactor;
    }

    /**
     * 获得计时的毫秒数
     * @readonly : number
     */
    get milliseconds() {
        return this.timedDate.getUTCMilliseconds() * this._countFactor;
    }


    //计时信息：结束






    //回调：开始


    /**
     * 用于回调的轮询时间间隔
     */
    protected _interval?:number|null|undefined;
    set interval(newValue){
        this._interval = newValue;
    }
    get interval(){
        return this._interval || 1000;
    }


    /**
     * 所有回调的停止函数
     */
    protected tickStops:(()=>void)[] = [];

    /**
     * 开始一个间隔回调，会在指定的时间间隔 interval 触发 listener
     * @param listener
     * @param interval
     */
    onTick(listener:(this:this,clocker:this)=>boolean,interval?:number){
        interval = interval || this.interval;

        const clearID = setInterval(()=>{
            if (listener.call(this,this)){
                stop();
            }
        },interval);

        const stop =  ()=>{
            clearInterval(clearID);
            const stopIndex = this.tickStops.indexOf(stop);
            if (stopIndex < 0){
                return;
            }
            this.tickStops.splice(stopIndex,1);
        };

        this.tickStops.push(stop);
        return stop;
    }

    /**
     * 关闭所有的间隔回调
     */
    offTick(){
        let lastIndex = this.tickStops.length - 1;
        while (lastIndex >= 0){
            this.tickStops[lastIndex]()
            lastIndex--;
        }
    }


    /**
     * 到达目标时间的事件监听者
     */
    protected targetListeners:((this:this,clocker:this)=>void)[] = [];

    /**
     * 添加到达目标时间事件的监听器
     * @param listener
     */
    onTarget(listener:(this:this,clocker:this)=>void){
        if (this.isReachedTarget){
            return
        }

        this.targetListeners.push(listener);
        this.launchListenReachTarget();
    }

    /**
     * 关闭所有的到达目标时间事件的监听器
     */
    offTarget(){
        if  (this.listenReachTargetClearID){
            this.stopListenReachTarget()
        }
        this.targetListeners = [];
    }

    /**
     * 监听到达目标时间定时器的ID
     */
    protected listenReachTargetClearID?:NodeJS.Timeout|null;

    /**
     * 启动监听到达目标
     */
    protected launchListenReachTarget(){
        if (this.listenReachTargetClearID || this.targetListeners.length === 0){
            return
        }
        this.listenReachTargetClearID = setInterval(()=>{
            if (this.isReachedTarget){
                this.executeTargetListeners();
                this.stopListenReachTarget();
            }
        },this.interval);
    }

    /**
     * 停止监听到达目标
     */
    protected stopListenReachTarget(){
        clearInterval(this.listenReachTargetClearID as NodeJS.Timeout);
        this.listenReachTargetClearID = null;
    }

    /**
     * 执行所有的到达目标监听者
     */
    protected executeTargetListeners(){
        for (const listener of this.targetListeners){
            listener.call(this,this);
        }
        this.targetListeners = [];
    }

    /**
     * 关闭所有的 间隔回调 和 到达目标回调
     */
    off(){
        this.offTick();
        this.offTarget();
    }
    //回调：结束

}



export default Clocker;